package org.NooLab.compare;

import org.NooLab.compare.utilities.math.*;


/**
 * 
 * this class is an entry point for various similarity calculations </br>
 * - items sets </br>
 * - feature vectors </br>
 * - contexts </br>
 * - text </br>
 * - strings </br>
 * - images </br>
 *  </br> 
 *  for each one there are several distance functions
 *  </br>
 *  it is controlled by a set of properties, e.g. for separating the data via array-copy,
 *  or for multi-threading if the feature vectors are large.
 *  
 *  Compare is used by Glue via the interface CompareIntf for similarity calculations;
 *  This interface is very simple
 *  
 *  *** planned: It may also be used to find the top-n of candidates for the most similar to a template from a table,
 *  *** or to create other similarity sortings starting with the smallest difference (global sort, or closest neighbor)
 * 
 *  
 */
class Compare implements CompareIntf{

	CompareFactory compFactory ;
	CompareProperties compProperties ;
	
	DistanceControlProperties distanceCtrl;
	Distance distance;
	
	int speedControl = -1;
	
	public Compare( CompareFactory cfactory, CompareProperties properties,  DistanceControlProperties distancectrl ){
		
		compFactory = cfactory ;
		compProperties = properties;
		 
		init(distanceCtrl);
		
	}
	
	public Compare( CompareFactory cfactory, CompareProperties properties){
	
		compFactory = cfactory ;
		compProperties = properties;
		
		distanceCtrl = new DistanceControlProperties() ;
		
		distanceCtrl.setDistanceMethod(compProperties.getDistanceMethod() );
		distanceCtrl.setMissingValueHandling( compProperties.getMissingValueHandling() ) ;

		distanceCtrl.setItemSetContrastalpha( compProperties.getMvItemSetContrastalpha() );
		distanceCtrl.setItemSetContrastbeta( compProperties.getMvItemSetContrastbeta() );
		distanceCtrl.setSurrogateDistance( compProperties.getSurrogateDistance() );
		
	
		
		init(distanceCtrl);
	}
	
	public Compare( CompareFactory cfactory ){
		
		compFactory = cfactory ;
		compProperties = new CompareProperties();
		
		distanceCtrl = new DistanceControlProperties() ;
		
		init(distanceCtrl);
	}

	
	private void init( DistanceControlProperties distancectrl ){
		
		distanceCtrl = distancectrl;
		// transfer settings
		
		
		// create distance calculator
		distance = new Distance( distanceCtrl );
	}
	
	// ========================================================================
	
	/**
	 *  calculating the similarity between two sets of strings
	 *  
	 */
	public double calculateSimilarity(String[] items1, String[] items2) {
	
		double result = CompareGlobals.__MV ;
		Distance _distance = distance ;
		
		if (speedControl < 0){
			_distance = new Distance( distanceCtrl ) ;
		} 
		
		try{
			
			if (items1.length !=  items2.length){
				// __MV_ByHardExcludLengthAdj ? remove positions that are too much
			}
			
			// this could be chained...
			_distance.setDomain( DistanceControlProperties._DD_SIMPLESET ) ; // == item sets
			_distance.setData( items1, items2 );
			
			// calculating the similarity between item sets ....
			// this will use the method as defined in instance of DistanceControlProperties
			// ATTENtION: note that set methods return the similarity (value ranges may exceed 1!!! [0 .. >[ ) not the distance
			result = _distance.calculate() ;
			  
			if (speedControl < 0){
				_distance = null;
			}
		}catch(Exception e){
			e.printStackTrace();
			result = CompareGlobals.__FAILED;
		}finally{
			
		}
		return result;
		 
	}

	/**
	 * distance between two plain numerical vectors, no features provided;
	 * vectors have to be of same length AND be aligned;
	 * if not of equal length, mv will be treated according 
	 * to settings (distance contribution [maybe, adjusted] ), or longer vector cut-off 
	 */
	public double calculateSimilarity( double[] vector1, double[] vector2 ){
		
		double result = CompareGlobals.__MV , d;
		int p,q ;
		
		try{
			

			 
			distance.setData( vector1, vector2 ).setDomain( DataDomainGlobals._DD_VECTOR );
			
			d = distance.calculate();
			
			// we should NOT normalize before inverting distance to similarity,
			// so, if the distance calc did it we have to revert it if we are going to normalize per position
			if (distanceCtrl.getNormalizedDistances() ){ 
				d = d * vector1.length ; // 0.107
			}
			
			result = Math.max( 0.0, 1 - d ) ; // d might be formalized my max of possible distance 
			
			
			// normalized by position, for contexts where vectors are of different length,
			// or if trans-model comparison is important 
			
			if (distanceCtrl.getNormalizedDistances() ){ 
				result = result/vector1.length ; 
			}
			
		}catch(Exception e){
			e.printStackTrace();
			result = CompareGlobals.__FAILED;
		}finally{
			
		}
		return result;
	}
	
	
	/**
	 * 
	 * feature vectors will be first aligned and distance will be calculated according to properties
	 * 
	 * @param features1
	 * @param vector1
	 * @param features2
	 * @param vector2
	 * @return
	 */
	public double calculateSimilarity( String[] features1, double[] vector1, 
			                           String[] features2, double[] vector2 ){
		
		double result = CompareGlobals.__MV ;
		
		try{
			
			
			distance.setReferenceFeatureVector( features1 ) ;
			
			
			distance.setData( vector1, vector2 ) ;
			distance.setDomain( DataDomainGlobals._DD_VECTOR );
			
			result = distance.calculate() ;
			
		}catch(Exception e){
			e.printStackTrace();
			result = CompareGlobals.__FAILED;
		}finally{
			
		}
		return result;
	}
	
	public double calculateSimilarity( String[] features2, double[] vector2 ){
		double d=0.0;
		
		Distance _distance = distance ;
		
		// in "strongly" parallelized processing we need separation, so we can 
		// afford a small overhead of creating objects...
		if (speedControl < 0){
			_distance = new Distance( distanceCtrl ) ;
		} 
	
		_distance.setDomain( DistanceControlProperties._DD_VECTOR ) ;
		_distance.setComparedFeatureVector( features2 ) ;
		
		d = _distance.calculate() ;
		
		if (speedControl < 0){
			_distance = null;
		}
		return d;
	}

	public double calculateSimilarity( double[] vector2 ){
		
		return calculateSimilarity( new String[]{}, vector2 );
	}
	
	 
	
	public DistanceControlProperties getDistanceCtrl() {
		return distanceCtrl;
	}
	
	public void setDistanceCtrl( DistanceControlProperties distancectrl ) {
		this.distanceCtrl = distancectrl;
		distance = null;
		distance = new Distance( distanceCtrl );
	}

	public int getSpeedControl() {
		return speedControl;
	}

	public void setSpeedControl(int speedControl) {
		this.speedControl = speedControl;
	}
	
	
}
